home *** CD-ROM | disk | FTP | other *** search
/ Enter 2006 September / Enter 09 2006.iso / Internet / SpamExperts Home 1.1 / SpamExperts Home.exe / lib / spamexperts.modules / ZODB / serialize.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-07-14  |  17.6 KB  |  605 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. '''Support for ZODB object serialization.
  5.  
  6. ZODB serializes objects using a custom format based on Python pickles.
  7. When an object is unserialized, it can be loaded as either a ghost or
  8. a real object.  A ghost is a persistent object of the appropriate type
  9. but without any state.  The first time a ghost is accessed, the
  10. persistence machinery traps access and loads the actual state.  A
  11. ghost allows many persistent objects to be loaded while minimizing the
  12. memory consumption of referenced but otherwise unused objects.
  13.  
  14. Pickle format
  15. -------------
  16.  
  17. ZODB stores serialized objects using a custom format based on pickle.
  18. Each serialized object has two parts: the class description and the
  19. object state.  The class description must provide enough information
  20. to call the class\'s ``__new__`` and create an empty object.  Once the
  21. object exists as a ghost, its state is passed to ``__setstate__``.
  22.  
  23. The class description can be in a variety of formats, in part to
  24. provide backwards compatibility with earlier versions of Zope.  The
  25. four current formats for class description are:
  26.  
  27.     1. type(obj)
  28.     2. type(obj), obj.__getnewargs__()
  29.     3. (module name, class name), None
  30.     7. (module name, class name), obj.__getnewargs__()
  31.  
  32. The second of these options is used if the object has a __getnewargs__()
  33. method.  It is intended to support objects like persistent classes that have
  34. custom C layouts that are determined by arguments to __new__().  The
  35. third and fourth (#3 & #7) apply to instances of a persistent class (which
  36. means the class itself is persistent, not that it\'s a subclass of
  37. Persistent).
  38.  
  39. The type object is usually stored using the standard pickle mechanism, which
  40. involves the pickle GLOBAL opcode (giving the type\'s module and name as
  41. strings).  The type may itself be a persistent object, in which case a
  42. persistent reference (see below) is used.
  43.  
  44. It\'s unclear what "usually" means in the last paragraph.  There are two
  45. useful places to concentrate confusion about exactly which formats exist:
  46.  
  47. - ObjectReader.getClassName() below returns a dotted "module.class"
  48.   string, via actually loading a pickle.  This requires that the
  49.   implementation of application objects be available.
  50.  
  51. - ZODB/utils.py\'s get_pickle_metadata() tries to return the module and
  52.   class names (as strings) without importing any application modules or
  53.   classes, via analyzing the pickle.
  54.  
  55. Earlier versions of Zope supported several other kinds of class
  56. descriptions.  The current serialization code reads these descriptions, but
  57. does not write them.  The three earlier formats are:
  58.  
  59.     4. (module name, class name), __getinitargs__()
  60.     5. class, None
  61.     6. class, __getinitargs__()
  62.  
  63. Formats 4 and 6 are used only if the class defines a __getinitargs__()
  64. method, but we really can\'t tell them apart from formats 7 and 2
  65. (respectively).  Formats 5 and 6 are used if the class does not have a
  66. __module__ attribute (I\'m not sure when this applies, but I think it occurs
  67. for some but not all ZClasses).
  68.  
  69.  
  70. Persistent references
  71. ---------------------
  72.  
  73. When one persistent object pickle refers to another persistent object,
  74. the database uses a persistent reference.
  75.  
  76. ZODB persistent references are of the form::
  77.  
  78. oid
  79.     A simple object reference.
  80.  
  81. (oid, class meta data)
  82.     A persistent object reference
  83.  
  84. [reference_type, args]
  85.     An extended reference
  86.  
  87.     Extension references come in a number of subforms, based on the
  88.     reference types.
  89.  
  90.     The following reference types are defined:
  91.  
  92.     \'w\'
  93.         Persistent weak reference.  The arguments consist of an oid.
  94.  
  95.     The following are planned for the future:
  96.  
  97.     \'n\'
  98.         Multi-database simple object reference.  The arguments consist
  99.         of a databaase name, and an object id.
  100.  
  101.     \'m\'
  102.         Multi-database persistent object reference.  The arguments consist
  103.         of a databaase name, an object id, and class meta data.
  104.  
  105. The following legacy format is also supported.
  106.     
  107. [oid]
  108.     A persistent weak reference
  109.  
  110. Because the persistent object reference forms include class
  111. information, it is not possible to change the class of a persistent
  112. object for which this form is used.  If a transaction changed the
  113. class of an object, a new record with new class metadata would be
  114. written but all the old references would still use the old class.  (It
  115. is possible that we could deal with this limitation in the future.)
  116.  
  117. An object id is used alone when a class requires arguments
  118. to it\'s __new__ method, which is signalled by the class having a
  119. __getnewargs__ attribute.
  120.  
  121. A number of legacyforms are defined:
  122.  
  123.  
  124. '''
  125. import cPickle
  126. import cStringIO
  127. import logging
  128. from persistent import Persistent
  129. from persistent.wref import WeakRefMarker, WeakRef
  130. from ZODB import broken
  131. from ZODB.broken import Broken
  132. from ZODB.POSException import InvalidObjectReference
  133. _oidtypes = (str, type(None))
  134.  
  135. def myhasattr(obj, name, _marker = object()):
  136.     """Make sure we don't mask exceptions like hasattr().
  137.  
  138.     We don't want exceptions other than AttributeError to be masked,
  139.     since that too often masks other programming errors.
  140.     Three-argument getattr() doesn't mask those, so we use that to
  141.     implement our own hasattr() replacement.
  142.     """
  143.     return getattr(obj, name, _marker) is not _marker
  144.  
  145.  
  146. class ObjectWriter:
  147.     '''Serializes objects for storage in the database.
  148.  
  149.     The ObjectWriter creates object pickles in the ZODB format.  It
  150.     also detects new persistent objects reachable from the current
  151.     object.
  152.     '''
  153.     _jar = None
  154.     
  155.     def __init__(self, obj = None):
  156.         self._file = cStringIO.StringIO()
  157.         self._p = cPickle.Pickler(self._file, 1)
  158.         self._p.persistent_id = self.persistent_id
  159.         self._stack = []
  160.         if obj is not None:
  161.             self._stack.append(obj)
  162.             jar = obj._p_jar
  163.             if not myhasattr(jar, 'new_oid'):
  164.                 raise AssertionError
  165.             self._jar = jar
  166.         
  167.  
  168.     
  169.     def persistent_id(self, obj):
  170.         """Return the persistent id for obj.
  171.  
  172.         >>> from ZODB.tests.util import P
  173.         >>> class DummyJar:
  174.         ...     def new_oid(self):
  175.         ...         return 42
  176.         ...     def db(self):
  177.         ...         return self
  178.         ...     databases = {}
  179.         
  180.         >>> jar = DummyJar()
  181.         >>> class O:
  182.         ...     _p_jar = jar
  183.         >>> writer = ObjectWriter(O)
  184.  
  185.         Normally, object references include the oid and a cached named
  186.         reference to the class.  Having the class information
  187.         available allows fast creation of the ghost, avoiding
  188.         requiring an additional database lookup.
  189.  
  190.         >>> bob = P('bob')
  191.         >>> oid, cls = writer.persistent_id(bob)
  192.         >>> oid
  193.         42
  194.         >>> cls is P
  195.         True
  196.  
  197.         If a persistent object does not already have an oid and jar,
  198.         these will be assigned by persistent_id():
  199.  
  200.         >>> bob._p_oid
  201.         42
  202.         >>> bob._p_jar is jar
  203.         True
  204.  
  205.         If the object already has a persistent id, the id is not changed:
  206.  
  207.         >>> bob._p_oid = 24
  208.         >>> oid, cls = writer.persistent_id(bob)
  209.         >>> oid
  210.         24
  211.         >>> cls is P
  212.         True
  213.  
  214.         If the jar doesn't match that of the writer, an error is raised:
  215.  
  216.         >>> bob._p_jar = DummyJar()
  217.         >>> writer.persistent_id(bob)
  218.         Traceback (most recent call last):
  219.           ...
  220.         InvalidObjectReference: Attempt to store an object from a foreign database connection
  221.  
  222.         Constructor arguments used by __new__(), as returned by
  223.         __getnewargs__(), can affect memory allocation, but may also
  224.         change over the life of the object.  This makes it useless to
  225.         cache even the object's class.
  226.  
  227.         >>> class PNewArgs(P):
  228.         ...     def __getnewargs__(self):
  229.         ...         return ()
  230.  
  231.         >>> sam = PNewArgs('sam')
  232.         >>> writer.persistent_id(sam)
  233.         42
  234.         >>> sam._p_oid
  235.         42
  236.         >>> sam._p_jar is jar
  237.         True
  238.  
  239.         Check that simple objects don't get accused of persistence:
  240.  
  241.         >>> writer.persistent_id(42)
  242.         >>> writer.persistent_id(object())
  243.  
  244.         Check that a classic class doesn't get identified improperly:
  245.  
  246.         >>> class ClassicClara:
  247.         ...    pass
  248.         >>> clara = ClassicClara()
  249.  
  250.         >>> writer.persistent_id(clara)
  251.         """
  252.         if not isinstance(obj, (Persistent, type, WeakRef)):
  253.             return None
  254.         
  255.         
  256.         try:
  257.             oid = obj._p_oid
  258.         except AttributeError:
  259.             return None
  260.  
  261.         if not oid is None or isinstance(oid, str):
  262.             if hasattr(oid, '__get__'):
  263.                 return None
  264.             
  265.             if oid is WeakRefMarker:
  266.                 oid = obj.oid
  267.                 if oid is None:
  268.                     obj = obj()
  269.                     oid = obj._p_oid
  270.                     if oid is None:
  271.                         oid = self._jar.new_oid()
  272.                         obj._p_jar = self._jar
  273.                         obj._p_oid = oid
  274.                         self._stack.append(obj)
  275.                     
  276.                 
  277.                 return [
  278.                     'w',
  279.                     (oid,)]
  280.             
  281.         
  282.         database_name = None
  283.         if oid is None:
  284.             oid = obj._p_oid = self._jar.new_oid()
  285.             obj._p_jar = self._jar
  286.             self._stack.append(obj)
  287.         elif obj._p_jar is not self._jar:
  288.             
  289.             try:
  290.                 otherdb = obj._p_jar.db()
  291.                 database_name = otherdb.database_name
  292.             except AttributeError:
  293.                 otherdb = self
  294.  
  295.             if self._jar.db().databases.get(database_name) is not otherdb:
  296.                 raise InvalidObjectReference('Attempt to store an object from a foreign database connection')
  297.             
  298.             if self._jar.get_connection(database_name) is not obj._p_jar:
  299.                 raise InvalidObjectReference('Attempt to store a reference to an object from a separate connection to the same database or multidatabase')
  300.             
  301.             if obj._p_jar._implicitlyAdding(oid):
  302.                 raise InvalidObjectReference("A new object is reachable from multiple databases. Won't try to guess which one was correct!")
  303.             
  304.         
  305.         klass = type(obj)
  306.         if hasattr(klass, '__getnewargs__'):
  307.             if database_name:
  308.                 return [
  309.                     'n',
  310.                     (database_name, oid)]
  311.             
  312.             return oid
  313.         
  314.         if database_name:
  315.             return [
  316.                 'm',
  317.                 (database_name, oid, klass)]
  318.         
  319.         return (oid, klass)
  320.  
  321.     
  322.     def serialize(self, obj):
  323.         klass = type(obj)
  324.         newargs = getattr(obj, '__getnewargs__', None)
  325.         if isinstance(getattr(klass, '_p_oid', 0), _oidtypes) and klass.__module__:
  326.             klass = (klass.__module__, klass.__name__)
  327.             if newargs is None:
  328.                 meta = (klass, None)
  329.             else:
  330.                 meta = (klass, newargs())
  331.         elif newargs is None:
  332.             meta = klass
  333.         else:
  334.             meta = (klass, newargs())
  335.         return self._dump(meta, obj.__getstate__())
  336.  
  337.     
  338.     def _dump(self, classmeta, state):
  339.         self._file.seek(0)
  340.         self._p.clear_memo()
  341.         self._p.dump(classmeta)
  342.         self._p.dump(state)
  343.         self._file.truncate()
  344.         return self._file.getvalue()
  345.  
  346.     
  347.     def __iter__(self):
  348.         return NewObjectIterator(self._stack)
  349.  
  350.  
  351.  
  352. class NewObjectIterator:
  353.     
  354.     def __init__(self, stack):
  355.         self._stack = stack
  356.  
  357.     
  358.     def __iter__(self):
  359.         return self
  360.  
  361.     
  362.     def next(self):
  363.         if self._stack:
  364.             elt = self._stack.pop()
  365.             return elt
  366.         else:
  367.             raise StopIteration
  368.  
  369.  
  370.  
  371. class ObjectReader:
  372.     
  373.     def __init__(self, conn = None, cache = None, factory = None):
  374.         self._conn = conn
  375.         self._cache = cache
  376.         self._factory = factory
  377.  
  378.     
  379.     def _get_class(self, module, name):
  380.         return self._factory(self._conn, module, name)
  381.  
  382.     
  383.     def _get_unpickler(self, pickle):
  384.         file = cStringIO.StringIO(pickle)
  385.         unpickler = cPickle.Unpickler(file)
  386.         unpickler.persistent_load = self._persistent_load
  387.         factory = self._factory
  388.         conn = self._conn
  389.         
  390.         def find_global(modulename, name):
  391.             return factory(conn, modulename, name)
  392.  
  393.         unpickler.find_global = find_global
  394.         return unpickler
  395.  
  396.     loaders = { }
  397.     
  398.     def _persistent_load(self, reference):
  399.         if isinstance(reference, tuple):
  400.             return self.load_persistent(*reference)
  401.         elif isinstance(reference, str):
  402.             return self.load_oid(reference)
  403.         else:
  404.             
  405.             try:
  406.                 (reference_type, args) = reference
  407.             except ValueError:
  408.                 return self.loaders['w'](self, *reference)
  409.  
  410.             return self.loaders[reference_type](self, *args)
  411.  
  412.     
  413.     def load_persistent(self, oid, klass):
  414.         obj = self._cache.get(oid, None)
  415.         if obj is not None:
  416.             return obj
  417.         
  418.         if isinstance(klass, tuple):
  419.             klass = self._get_class(*klass)
  420.         
  421.         if issubclass(klass, Broken):
  422.             if not issubclass(klass, broken.PersistentBroken):
  423.                 klass = broken.persistentBroken(klass)
  424.             
  425.         
  426.         
  427.         try:
  428.             obj = klass.__new__(klass)
  429.         except TypeError:
  430.             return self._conn.get(oid)
  431.  
  432.         obj._p_oid = oid
  433.         obj._p_jar = self._conn
  434.         obj._p_changed = None
  435.         self._cache[oid] = obj
  436.         return obj
  437.  
  438.     
  439.     def load_multi_persistent(self, database_name, oid, klass):
  440.         conn = self._conn.get_connection(database_name)
  441.         reader = ObjectReader(conn, conn._cache, self._factory)
  442.         return reader.load_persistent(oid, klass)
  443.  
  444.     loaders['m'] = load_multi_persistent
  445.     
  446.     def load_persistent_weakref(self, oid):
  447.         obj = WeakRef.__new__(WeakRef)
  448.         obj.oid = oid
  449.         obj.dm = self._conn
  450.         return obj
  451.  
  452.     loaders['w'] = load_persistent_weakref
  453.     
  454.     def load_oid(self, oid):
  455.         obj = self._cache.get(oid, None)
  456.         if obj is not None:
  457.             return obj
  458.         
  459.         return self._conn.get(oid)
  460.  
  461.     
  462.     def load_multi_oid(self, database_name, oid):
  463.         conn = self._conn.get_connection(database_name)
  464.         reader = ObjectReader(conn, conn._cache, self._factory)
  465.         return reader.load_oid(oid)
  466.  
  467.     loaders['n'] = load_multi_oid
  468.     
  469.     def _new_object(self, klass, args):
  470.         if not args and not myhasattr(klass, '__getnewargs__'):
  471.             obj = klass.__new__(klass)
  472.         else:
  473.             obj = klass(*args)
  474.             if not isinstance(klass, type):
  475.                 obj.__dict__.clear()
  476.             
  477.         return obj
  478.  
  479.     
  480.     def getClassName(self, pickle):
  481.         unpickler = self._get_unpickler(pickle)
  482.         klass = unpickler.load()
  483.         if isinstance(klass, tuple):
  484.             (klass, args) = klass
  485.             if isinstance(klass, tuple):
  486.                 return '%s.%s' % klass
  487.             
  488.         
  489.         return '%s.%s' % (klass.__module__, klass.__name__)
  490.  
  491.     
  492.     def getGhost(self, pickle):
  493.         unpickler = self._get_unpickler(pickle)
  494.         klass = unpickler.load()
  495.         if isinstance(klass, tuple):
  496.             (klass, args) = klass
  497.             if isinstance(klass, tuple):
  498.                 klass = self._get_class(*klass)
  499.             
  500.             if args is None:
  501.                 args = ()
  502.             
  503.         else:
  504.             args = ()
  505.         if issubclass(klass, Broken):
  506.             if not issubclass(klass, broken.PersistentBroken):
  507.                 klass = broken.persistentBroken(klass)
  508.             
  509.         
  510.         return klass.__new__(klass, *args)
  511.  
  512.     
  513.     def getState(self, pickle):
  514.         unpickler = self._get_unpickler(pickle)
  515.         
  516.         try:
  517.             unpickler.load()
  518.             return unpickler.load()
  519.         except EOFError:
  520.             msg = None
  521.             log = logging.getLogger('ZODB.serialize')
  522.             log.exception('Unpickling error: %r', pickle)
  523.             raise 
  524.  
  525.  
  526.     
  527.     def setGhostState(self, obj, pickle):
  528.         state = self.getState(pickle)
  529.         obj.__setstate__(state)
  530.  
  531.  
  532. oid_loaders = {
  533.     'w': (lambda oid: pass) }
  534.  
  535. def referencesf(p, oids = None):
  536.     '''Return a list of object ids found in a pickle
  537.  
  538.     A list may be passed in, in which case, information is
  539.     appended to it.
  540.  
  541.     Weak references are not included.
  542.     '''
  543.     refs = []
  544.     u = cPickle.Unpickler(cStringIO.StringIO(p))
  545.     u.persistent_load = refs
  546.     u.noload()
  547.     u.noload()
  548.     if oids is None:
  549.         oids = []
  550.     
  551.     for reference in refs:
  552.         if isinstance(reference, tuple):
  553.             oid = reference[0]
  554.         elif isinstance(reference, str):
  555.             oid = reference
  556.         else:
  557.             
  558.             try:
  559.                 (reference_type, args) = reference
  560.             except ValueError:
  561.                 continue
  562.  
  563.             oid = oid_loaders[reference_type](*args)
  564.         if oid:
  565.             oids.append(oid)
  566.             continue
  567.     
  568.     return oids
  569.  
  570. oid_klass_loaders = {
  571.     'w': (lambda oid: pass) }
  572.  
  573. def get_refs(a_pickle):
  574.     """Return oid and class information for references in a pickle
  575.  
  576.     The result of a list of oid and class information tuples.
  577.     If the reference doesn't contain class information, then the
  578.     klass information is None.
  579.     """
  580.     refs = []
  581.     u = cPickle.Unpickler(cStringIO.StringIO(a_pickle))
  582.     u.persistent_load = refs
  583.     u.noload()
  584.     u.noload()
  585.     result = []
  586.     for reference in refs:
  587.         if isinstance(reference, tuple):
  588.             data = reference
  589.         elif isinstance(reference, str):
  590.             data = (reference, None)
  591.         else:
  592.             
  593.             try:
  594.                 (reference_type, args) = reference
  595.             except ValueError:
  596.                 continue
  597.  
  598.             data = oid_klass_loaders[reference_type](*args)
  599.         if data:
  600.             result.append(data)
  601.             continue
  602.     
  603.     return result
  604.  
  605.